/*
cAndy Apple Core: An Apple //e emulator engine
    - based almost entirely on AppleWin

Copyright (C) 1994-1996, Michael O'Brien
Copyright (C) 1999-2001, Oliver Schmidt
Copyright (C) 2002-2005, Tom Charlesworth
Copyright (C) 2006-2008, Tom Charlesworth, Michael Pohoreski, Nick Westgate
Copyright (C) 2009-2010, Garnet Ulrich

cAndy Apple Core is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

cAndy Apple Core is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with cAndy Apple Core; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "StdAfx.h"

#define MAX_SAMPLES (16*1024)

#define  SOUND_NONE    0
#define  SOUND_DIRECT  1
#define  SOUND_SMART   2
#define  SOUND_WAVE    3

//-------------------------------------

BYTE*	g_pSpeakerBuffer = NULL;

// Globals (SOUND_WAVE)
const BYTE		SPKR_DATA_INIT = 0x80;

BYTE	g_nSpeakerData	= SPKR_DATA_INIT;
UINT		g_nBufferIdx	= 0;

static BYTE*	g_pRemainderBuffer = NULL;
static UINT		g_nRemainderBufferSize;		// Setup in SpkrInitialize()
static UINT		g_nRemainderBufferIdx;		// Setup in SpkrInitialize()


// Application-wide globals:
DWORD			soundtype		= SOUND_WAVE;
double		    g_fClksPerSpkrSample;		// Setup in SetClksPerSpkrSample()

// Globals
static DWORD	lastcyclenum	= 0;
static DWORD	toggles			= 0;
static unsigned __int64 g_nSpkrQuietCycleCount = 0;
static unsigned __int64 g_nSpkrLastCycle = 0;
static bool g_bSpkrToggleFlag = false;
static bool g_bSpkrAvailable = false;

//-----------------------------------------------------------------------------

// Forward refs:
static void    Spkr_SetActive(bool bActive);

//=============================================================================

static void SetClksPerSpkrSample()
{
    //	// 23.191 clks for 44.1Khz (when 6502 CLK=1.0Mhz)
    //	g_fClksPerSpkrSample = g_fCurrentCLK6502 / (double)SPKR_SAMPLE_RATE;

    // Use integer value: Better for MJ Mahon's RT.SYNTH.DSK (integer multiples of 1.023MHz Clk)
    // . 23 clks @ 1.023MHz
    g_fClksPerSpkrSample = (double) (UINT) (g_fCurrentCLK6502 / (double)SPKR_SAMPLE_RATE);
}

//=============================================================================

static void InitRemainderBuffer()
{
    delete [] g_pRemainderBuffer;

    SetClksPerSpkrSample();

    g_nRemainderBufferSize = (UINT) g_fClksPerSpkrSample;
    if ((double)g_nRemainderBufferSize != g_fClksPerSpkrSample)
        g_nRemainderBufferSize++;

    g_pRemainderBuffer = new BYTE [g_nRemainderBufferSize];
    memset(g_pRemainderBuffer, 0, g_nRemainderBufferSize);

    g_nRemainderBufferIdx = 0;
}

//
// ----- ALL GLOBALLY ACCESSIBLE FUNCTIONS ARE BELOW THIS LINE -----
//

//=============================================================================

void SpkrInitialize ()
{
    InitRemainderBuffer();
    g_pSpeakerBuffer = new BYTE [SPKR_SAMPLE_RATE];	// Buffer can hold a max of 1 seconds worth of samples
}

//=============================================================================

static void InitRemainderBuffer(UINT nCyclesRemaining)
{
    if(nCyclesRemaining == 0)
        return;

    for(g_nRemainderBufferIdx=0; g_nRemainderBufferIdx<nCyclesRemaining; g_nRemainderBufferIdx++)
        g_pRemainderBuffer[g_nRemainderBufferIdx] = g_nSpeakerData;

}

static void UpdateRemainderBuffer(ULONG* pnCycleDiff)
{
    if(g_nRemainderBufferIdx)
    {
        while((g_nRemainderBufferIdx < g_nRemainderBufferSize) && *pnCycleDiff)
        {
            g_pRemainderBuffer[g_nRemainderBufferIdx] = g_nSpeakerData;
            g_nRemainderBufferIdx++;
            (*pnCycleDiff)--;
        }

        if(g_nRemainderBufferIdx == g_nRemainderBufferSize)
        {
            g_nRemainderBufferIdx = 0;
            signed long nSampleMean = 0;
            for(UINT i=0,nCnt=0; i<g_nRemainderBufferSize; i++)
                nSampleMean += (signed long) g_pRemainderBuffer[i];
            nSampleMean /= (signed long) g_nRemainderBufferSize;

            if(g_nBufferIdx < SPKR_SAMPLE_RATE-1)
                g_pSpeakerBuffer[g_nBufferIdx++] = (BYTE) nSampleMean;
        }
    }
}

static void UpdateSpkr() //gu - this is where the buffer gets filled
{
    ULONG nCycleDiff = (ULONG) (g_nCumulativeCycles - g_nSpkrLastCycle);

    UpdateRemainderBuffer(&nCycleDiff);

    ULONG nNumSamples = (ULONG) ((double)nCycleDiff / g_fClksPerSpkrSample);

    ULONG nCyclesRemaining = (ULONG) ((double)nCycleDiff - (double)nNumSamples * g_fClksPerSpkrSample);

    while((nNumSamples--) && (g_nBufferIdx < SPKR_SAMPLE_RATE-1))
        g_pSpeakerBuffer[g_nBufferIdx++] = g_nSpeakerData;

    InitRemainderBuffer(nCyclesRemaining);	// Partially fill 1Mhz sample buffer

    g_nSpkrLastCycle = g_nCumulativeCycles;
}

//=============================================================================

// Called by emulation code when Speaker I/O reg is accessed
//


BYTE SpkrToggle (WORD, WORD, BYTE, BYTE, ULONG nCyclesLeft)
{
    g_bSpkrToggleFlag = true;

    Spkr_SetActive(true);

    CpuCalcCycles(nCyclesLeft);

    UpdateSpkr();

    g_nSpeakerData = ~g_nSpeakerData;

    return MemReadFloatingBus(nCyclesLeft);
}

//=============================================================================

// Called by ContinueExecution()
void SpkrUpdate (DWORD totalcycles)
{
    if(!g_bSpkrToggleFlag) {
        if(!g_nSpkrQuietCycleCount) {
            g_nSpkrQuietCycleCount = g_nCumulativeCycles;
        } else if(g_nCumulativeCycles - g_nSpkrQuietCycleCount > (unsigned __int64)g_fCurrentCLK6502/5) {
            // After 0.2 sec of Apple time, deactivate spkr voice
            // . This allows emulator to auto-switch to full-speed g_nAppMode for fast disk access
            Spkr_SetActive(false);
        }
    } else {
        g_nSpkrQuietCycleCount = 0;
        g_bSpkrToggleFlag = false;
    }

    UpdateSpkr();
} //end SpkrUpdate

//=============================================================================

static DWORD dwByteOffset = (DWORD)-1;
static int nNumSamplesError = 0;
static int nDbgSpkrCnt = 0;

//-----------------------------------------------------------------------------

static bool g_bSpkrRecentlyActive = false;

static void Spkr_SetActive(bool bActive)
{
    if(bActive){
        g_bSpkrRecentlyActive = true;
    } else {
        g_bSpkrRecentlyActive = false;
    }
}

//=============================================================================

DWORD SpkrGetSnapshot(SS_IO_Speaker* pSS)
{
    pSS->g_nSpkrLastCycle = g_nSpkrLastCycle;
    return 0;
}

DWORD SpkrSetSnapshot(SS_IO_Speaker* pSS)
{
    g_nSpkrLastCycle = pSS->g_nSpkrLastCycle;

    g_nBufferIdx = 0; //flush the sound buffer
    return 0;
}

